// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: t -*-

/*
mySerial0.h
  
Author:     Uwe Gartmann

This firmware is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
*/

#include <string.h>
#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include "wiring.h"
#include "mySerial0.h"

// Use same buffer size for RX and TX
#define BUFFER_SIZE 128
#define BUFFER_MASK (BUFFER_SIZE-1)

// check if the buffer size is the power of 2
#if ( BUFFER_SIZE & BUFFER_MASK )
#    error buffer size is not a power of 2
#endif

//define ringbuffer
struct ring_buffer {
  unsigned char buffer[BUFFER_SIZE];
  volatile unsigned char head;
  volatile unsigned char tail;
};

// create buffer for rx and tx
ring_buffer rx_buffer = { { 0 }, 0, 0 };
ring_buffer tx_buffer = { { 0 }, 0, 0 };

ISR(USART0_RX_vect)		//################################################
{
	unsigned char c = UDR0;
	unsigned char i = (rx_buffer.head + 1);
	i &= BUFFER_MASK;
	if (i != rx_buffer.tail)
	{
		rx_buffer.buffer[rx_buffer.head] = c;
		rx_buffer.head = i;
	}
}

ISR(USART0_UDRE_vect)	//################################################
{
    if (tx_buffer.head == tx_buffer.tail) {
        // Buffer is empty, disable the interrupt
        UCSR0B &= ~(1<<UDRIE0);
    } else {
    	UDR0 = tx_buffer.buffer[tx_buffer.tail];
    	tx_buffer.tail = (tx_buffer.tail + 1);
		tx_buffer.tail &= BUFFER_MASK;
    }
}

mySerial0::mySerial0()
{
// Class constructor
}

// Public Methods //////////////////////////////////////////////////////////////
void mySerial0::begin(long BAUD0)
{
#ifndef F_CPU
	#define F_CPU 16000000
#endif
#define _UBRR0 (F_CPU/8/BAUD0-1)	//when U2X0 is not set use divider 16 instead of 8

	// Set baudrate
	UCSR0A = (1<<U2X0); //U2X0 enabled!
	UBRR0H=(_UBRR0>>8);   //high byte
	UBRR0L=_UBRR0;      //low byte

	// Set frame format: 8data, no parity, 1stop bit
	UCSR0C = (0<<UMSEL00)|(0<<UPM00)|(0<<USBS0)|(3<<UCSZ00);

	// Enable receiver and transmitter
	UCSR0B = (1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0);
}

void mySerial0::end()
{
	// Enable receiver and transmitter
	UCSR0B = (0<<RXEN0)&(0<<TXEN0)&(0<<RXCIE0);
}

int mySerial0::available(void)
{
	uint8_t c =  BUFFER_SIZE + rx_buffer.head - rx_buffer.tail;
	return c &= BUFFER_MASK;
 }

int mySerial0::read(void)
{
	// if the head isn't ahead of the tail, we don't have any characters
	if (rx_buffer.head == rx_buffer.tail) {
		return -1;
	} else {
		unsigned char c = rx_buffer.buffer[rx_buffer.tail];
		rx_buffer.tail = (rx_buffer.tail + 1);
		rx_buffer.tail &= BUFFER_MASK;
    return c;
  }
}

void mySerial0::flush()
{
  // don't reverse this or there may be problems if the RX interrupt
  // occurs after reading the value of rx_buffer_head but before writing
  // the value to rx_buffer_tail; the previous value of rx_buffer_head
  // may be written to rx_buffer_tail, making it appear as if the buffer
  // don't reverse this or there may be problems if the RX interrupt
  // occurs after reading the value of rx_buffer_head but before writing
  // the value to rx_buffer_tail; the previous value of rx_buffer_head
  // may be written to rx_buffer_tail, making it appear as if the buffer
  // were full, not empty.
  rx_buffer.head = rx_buffer.tail;
  //_tx_buffer->head = _tx_buffer->tail;
}

void mySerial0::write(uint8_t c)
{
	unsigned char i = (tx_buffer.head + 1);
	unsigned char _tail = tx_buffer.tail;
	//if (_tail != tx_buffer.tail) {
	//	_tail = tx_buffer.tail;
	//}
	i &= BUFFER_MASK;
	if (i != _tail)
	{
		tx_buffer.buffer[tx_buffer.head] = c;
		tx_buffer.head = i;
	}
	UCSR0B |= (1<<UDRIE0); // Enable Data Register Empty interrupt
}

// Preinstantiate Objects //////////////////////////////////////////////////////
//mySerial0 Serial10;

